#!/usr/bin/env python
# encoding: utf-8

# @Author  : vfengcc
# @File    : gradebook.py
# @Time    : 2020/3/11 00:34

'''
本周作业内容如下（3.2-3.15）
编写一个学生管理系统类 ---Gradebook:
    要求能动态的添加学生-name和学生成绩 grades(一个学生可以有个多科的科目成绩)，并且根据学生名字能算出学生的平均成绩：
    提示：可以预习初始化一个字典，该实例字典存储相应的信息
    1 编写一个表示科目的类--Subject，该类包含一系列的考试成绩
    2.编写一个表示学生的类--Student， 该类包含此学生正在学习的各项课程
    3.编写一个包含所有学生考试成绩的容器类--Gradebook，该容易以学生的名字为key，并且可以动态的添加学生
'''

import random

SUBJECTS_DEFAULT = ['chinese', 'english']       # 默认可用的课程


class Subject:
    def __init__(self, subject, score):
        self.name = subject
        self.score = score

    def __str__(self):
        return '<Subject: {} - {}>'.format(self.name, self.score)

    __repr__ = __str__


class Student:
    def __init__(self, name, subjects=[]):
        self.name = name
        if not subjects:
            self.able_subjects = SUBJECTS_DEFAULT
        else:
            self.able_subjects = subjects


class Gradebook:
    def __init__(self):
        self.stu_score_dict = {}

    def __setitem__(self, name, subjects):
        if name not in self.stu_score_dict:
            self.stu_score_dict[name] = Student(name)

        for subject in subjects:
            if subject.name not in self.stu_score_dict[name].able_subjects:
                print('{} 课程没有开放'.format(subject.name))
                continue
            # 给student实例 设置属性
            setattr(self.stu_score_dict[name], subject.name, subject.score)
        self.__update_stu(name)  # 计算平均值

    def __getitem__(self, name):
        try:
            return self.stu_score_dict[name]
        except Exception as e:
            return self.__missing__(name)

    def __missing__(self, name):
        # print('未定义的"{}"'.format(name))
        return None

    def __delitem__(self, name):
        del self.stu_score_dict[name]

    # 获取学生的平均成绩
    def __avg_by_student(self, name):
        subject_scores = 0  # 课程总分
        subject_nums = 0  # 课程数量
        stu: Student = self.__getitem__(name)
        if stu:
            # 学生类 可用的课程列表
            for subject in stu.able_subjects:
                if hasattr(stu, subject):  # 是否存在课程属性
                    subject_scores += getattr(stu, subject)
                    subject_nums += 1
            return subject_scores / subject_nums
        return None

    # 添加/更新 课程成绩后，更新stu实例的 avg 属性
    def __update_stu(self, key):
        avg = self.__avg_by_student(key)
        setattr(self.stu_score_dict[key], 'avg', avg)

    # 公共 - 打印信息
    def __print_student(self, name='', stu=None):
        if not stu and name != '':
            stu = self.__getitem__(name)
        if stu:
            line = ['姓名-' + stu.name]
            for subject in stu.able_subjects:
                # 是否存在课程属性
                if hasattr(stu, subject):
                    line.append('{} - {}'.format(subject, getattr(stu, subject)))
            line.append('avg - {}'.format(getattr(stu, 'avg')))
            print(*line, sep='\t')
        else:
            print('{} 不存在'.format(name))

    # 输出单个学生的信息
    def show_student(self, key):
        print('【查询 {} 结果】:'.format(key))
        self.__print_student(key)

    # 输出所有学生的信息
    def show_student_all(self):
        print('【学生成绩列表】')
        for name, stu in self.stu_score_dict.items():
            self.__print_student(stu=stu)

    # 获取某一课程的平均分
    def avg_by_subject(self, subject):
        subject_scores = 0
        subject_students = 0
        for name, stu in self.stu_score_dict.items():
            # 是否存在课程属性
            if hasattr(stu, subject):
                subject_scores += getattr(stu, subject)
                subject_students += 1
        avg = subject_scores / subject_students
        print('{} - 平均分数:{}'.format(subject, avg))
        return avg


if __name__ == '__main__':
    def show_menu():
        print('=====1-添加学生及成绩, 2-查询, 3-列表')
        menu = input('输入选项>>>:').strip()
        if menu == '1':
            add()
        elif menu == '2':
            search()
        elif menu == '3':
            list()

    def add():
        print('\n---------1-添加学生及成绩-------------')
        while True:
            arg_name = input('>>>请输入学生姓名(返回请输q):').strip()
            if arg_name != '':
                # 返回菜单项
                if arg_name == 'q':
                    show_menu()
                    break
                # 如果存在，则展示出来
                if stuScore[arg_name]:
                    stuScore.show_student(arg_name)
                    print('-' * 50)
                # 创建student对象 接收成绩
                stu = Student(arg_name)
                while True:
                    arg_scores = input('>>>请依次输入"{}"课程的成绩(以空格隔开,返回输"q"):'.format(' '.join(stu.able_subjects))).strip()
                    if arg_scores != '':
                        # 返回上一级
                        if arg_scores == 'q':
                            break
                        else:
                            subjects = []
                            try:
                                for (k, v) in zip(stu.able_subjects, arg_scores.split(' ')):
                                    subjects.append(Subject(k, int(v)))
                                stuScore[stu.name] = subjects
                                print('操作成功：')
                                stuScore.show_student(stu.name)
                                print('-' * 50)
                                break
                            except Exception as e:
                                print(e)
                                print('数据异常，请检查：')

    def search():
        print('\n---------2-查询-------------')
        while True:
            arg_name = input('>>>请输入要查询的学生姓名(返回请输q):')
            if arg_name == 'q':
                show_menu()
                break

            stuScore.show_student(arg_name)

    def list():
        print('\n---------3-列表-------------')
        stuScore.show_student_all()
        show_menu()

    stuScore = Gradebook()

    # 快速填充数据
    for x in range(5):
        stu1_1 = Subject('chinese', random.randint(60, 100))
        stu1_2 = Subject('english', random.randint(60, 100))
        stuScore['stu{}'.format(x)] = [stu1_1, stu1_2]

    show_menu()

# 参考下 尹璐同学的代码